import string
import random
import operator


class MonoCipher:
    def __init__(self):
        self.keys = [chr(i) for i in range(32,127)]
        self.values = list(self.keys)
        
        random.seed(2)
        
        random.shuffle(self.keys)
        self.map = dict(zip(self.keys, self.values))

    def encrypt(self, message):
        string = ''

        for char in message:
            string += self.map.get(char)
            
        self.mangle(message)    
        self.change_key()

        return string

    def decrypt(self, message):
        string = ''
        
        self.mangle(message)

        for char in message:
            for key, value in self.map.iteritems():
                if value == char:
                    string += key
                    
        self.change_key()

        return string
        
    def change_key(self):
        random.shuffle(self.keys)
        self.map = dict(zip(self.keys, self.values))
    
    def mangle(self, message):
        a = range(len(message))
        random.shuffle(a)
        b = list(message)
        
        for i in range(len(message)):
            b[i], b[a[i]] = b[a[i]], b[i]
        
        message = ''.join(b)
    
   
    def crack_string(self, message):
        freq = dict()
        key = dict()
        dictionary = list()
        
        for x in range(26):
            dictionary.append(list())
            
        with open("/usr/share/dict/words") as file:
            for word in file:
                word = word.strip().lower()
                if(len(word) < 25):
                    dictionary[len(word)].append(word)
            
        file.close()
           
        for char in message:
            if freq.get(char, None) == None:
                freq[char] = 1;
            else:
                freq[char] = freq[char] + 1;

        freqs = sorted(freq.iteritems(), key=operator.itemgetter(1))
        space = freqs[len(freqs) - 1][0]
        e = freqs[len(freqs) - 2][0]
        
        key[space] = ' '
        key[e] = 'e'
        
        words = message.split(space)   
        
        for word in words:
            if len(word) == 1:
                key[word[0]] = 'a'
            if len(word) == 3:
                if key.get(word[2], ' ') == 'e':
                    key[word[0]] = 't'
                    key[word[1]] = 'h'
    
        s = ""
        for word in words:
            if len(word) < len(dictionary):
                if self.keyword(word, key) not in dictionary[len(word)]:
                    m = self.matches(word, dictionary[len(word)], key)
                    if len(m) > 0:
                        s += m[random.randint(0, len(m) - 1)] + ' '
                    else:
                        s += word + ' '
                else:
                    s += self.keyword(word, key) + ' '
            else:
                s += word + ' '

        return s
        
    def matches(self, word, words, key):
        m = list()
        for w in words:
            add = True
            for i, c in enumerate(word):
                if c in key and key.get(c, ' ') != w[i]:
                    add = False
                    break;
                if i != len(word) - 1:    
                    if(word[i] == word[i+1] and w[i] != w[i+1]):
                        add = False
                        break;
            if add:
                m.append(w)
        return m
                
    def keyword(self, word, key):
        s = ""
        for c in word:
            if key.get(c, None) != None:
                s += key[c]
            else:
                s += c
        
        return s;     
